package lac.serviceuint;

import java.io.InputStream;
import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.XStream;

import lac.model.core.persistent.Session;
import lac.model.core.persistent.SessionFactory;
import lac.serviceuint.cfg.BeanMode;
import lac.serviceuint.cfg.Logic;
import lac.serviceuint.cfg.Method;
import lac.serviceuint.cfg.MethodParam;
import lac.serviceuint.cfg.ServiceUnit;
import lac.serviceuint.cfg.model.ServiceUnitModelDescribe;
import lac.serviceuint.handler.LogicHandler;
import lac.serviceuint.handler.MethodHandler;
import lac.serviceuint.handler.MethodSignature;
import lac.serviceuint.interceptor.LogicInterceptor;
import lac.serviceuint.interceptor.MethodInterceptor;
import lac.serviceuint.job.cfg.ServiceUnitJob;
import lac.serviceuint.tool.ApplicationContextBean;
import lac.serviceuint.tool.HttpUtil;

public class ServiceUnitContext {

	private static Logger log = LoggerFactory.getLogger(ServiceUnitContext.class);
	
	@Autowired(required=false)
	private SessionFactory sessionFactory;
	@Autowired(required=false)
	private ApplicationContextBean applicationContextBean;
	
	public static ThreadLocal<Object> methodParamsMapContext = new ThreadLocal<Object>();
	public static ThreadLocal<List<MethodInterceptor>> methodInterceptorsContext = new ThreadLocal<List<MethodInterceptor>>();
	public static ThreadLocal<Map<Class<? extends Session>,Session>> sessionContext = new ThreadLocal<Map<Class<? extends Session>,Session>>(); 
	public static ThreadLocal<Map<Object,Object>> varMapContext = new ThreadLocal<Map<Object,Object>>(); 
	public static ThreadLocal<Object> resultContext = new ThreadLocal<Object>(); 
	public static ThreadLocal<LogicHandlerControlSignal> logicHandlerControlSignalContext = new ThreadLocal<LogicHandlerControlSignal>(); 
	
	private Map<String,ServiceUnit> serviceUnitContext = new LinkedHashMap<String,ServiceUnit>();
	private Map<String,MethodHandler> serviceUnitMethodContext = new LinkedHashMap<String,MethodHandler>();
	
	private List<MethodInterceptor> methodInterceptors = new ArrayList<MethodInterceptor>();
	
	private List<LogicInterceptor> logicInterceptors = new ArrayList<LogicInterceptor>();
	
	private Map<String,Method> registryMethodContext = new LinkedHashMap<>();
	
	public static XStream xstream = new XStream();
	
	private boolean enableRegister = false;
	private String registerAddress;
	private String serviceId;
	private String host;
	private Integer port;
	private Long methodHeartBeatTime = 1000*10L;
	private Thread methodHeartBeatThread = new Thread(new RegistryServiceMethodHeartBeatThread(),"RegistryServiceMethodHeartBeatThread") ;
	
	static{
		xstream.processAnnotations(ServiceUnit.class);
		xstream.processAnnotations(ServiceUnitJob.class);
		xstream.setMode(XStream.NO_REFERENCES);
	}
	
	
	public void addServiceUnit(InputStream inputStream){
		ServiceUnit serviceUnit= (ServiceUnit) xstream.fromXML(inputStream);
		addServiceUnit(serviceUnit);
	}
	
	public void addServiceUnit(ServiceUnit serviceUnit){
		String serviceUnitName = serviceUnit.getName();
		serviceUnitContext.put(serviceUnitName, serviceUnit);
		log.info("加载ServiceUnit:"+serviceUnitName);
		List<Method> methods = serviceUnit.getMethods();
		for(Method method : methods){
			method.setServiceUnit(serviceUnit);
			MethodHandler methodHandler = new MethodHandler();
			methodHandler.setMethod(method);
			methodHandler.setMethodParams(instanceMethodParam(method));
			List<LogicHandler> logicHandlers = methodHandler.getLogicHandlers();
			List<Logic> logics = method.getLogics();
			if(logics != null){
				Collections.sort(logics, new Comparator<Logic>(){
					@Override
					public int compare(Logic o1, Logic o2) {
						if(o1.getSort()!= null && o2.getSort() != null){
							return o1.getSort().compareTo(o2.getSort());
						}else{
							return 0;
						}
					}
					
				});
				for(Logic logic : logics){
					logic.setMethod(method);
					LogicHandler logicHandlerBean = instanceLogicHandler(logic);
					if(logicHandlerBean != null){
						logicHandlers.add(logicHandlerBean);
					}
				}
			}
			log.debug(method.getName());
			String serviceUnitMethodName = serviceUnitName+"_"+method.getName();
			serviceUnitMethodContext.put(serviceUnitMethodName, methodHandler);
			if(enableRegister && method.getRegister()!= null && method.getRegister() && serviceId != null){
				registryMethodContext.put(serviceUnitMethodName, method);
			}
		}
	}
	
	public void addServiceUnit(List<ServiceUnit> serviceUnitList){
		if(serviceUnitList != null && serviceUnitList.size()>0){
			for(ServiceUnit serviceUnit : serviceUnitList){
				this.addServiceUnit(serviceUnit);
			}
		}
	}
	
	
	private List<MethodParam> instanceMethodParam(Method method){
		try{
			String signatureJavaClass = method.getSignatureJavaClass();
			Map<String,MethodParam> methodParamMap= new LinkedHashMap<String,MethodParam>();
			List<MethodParam> methodParams = new ArrayList<MethodParam>();
			if(null != signatureJavaClass && !signatureJavaClass.equals("")){
				Class<?> clazz = Class.forName(signatureJavaClass);
				MethodSignature methodSignature =  (MethodSignature) clazz.newInstance();
				methodParams.addAll(methodSignature.getMethodParams());
			}
			if(method.getParams()!=null){
				methodParams.addAll(method.getParams());
			}
			for(MethodParam methodParam : methodParams){
				methodParamMap.put(methodParam.getName(), methodParam);
				methodParam.setMethod(method);
			}
			methodParams.clear();
			methodParams.addAll(methodParamMap.values());
			return methodParams;
		}catch(Throwable t){
			log.warn("ServiceUnit:"+method.getServiceUnit().getName()+"method:"+method.getName()+"加载失败！");
			return new ArrayList<MethodParam>();
		}
	}
	
	private LogicHandler instanceLogicHandler(Logic logic){
		try{
			String className = logic.getJavaClassName();
			Class<?> clazz = Class.forName(className);
			LogicHandler logicHandler = new LogicHandler();
			logicHandler.setLogic(logic);
			LogicBean logicBean = null;
			if(logic.getBeanMode().equals(BeanMode.New.name())){
				logicBean = (LogicBean) clazz.newInstance();
			}else if(logic.getBeanMode().equals(BeanMode.Spring.name())){
				logicBean = (LogicBean) applicationContextBean.getApplicationContext().getBean(clazz);
			}
			logicHandler.setLogic(logic);
			logicHandler.setLogicBean(logicBean);
			return logicHandler;
		}catch(Throwable t){
			log.warn("加载："+logic.getMethod().getServiceUnit().getName()+"."+logic.getMethod().getName()+"["+logic.getJavaClassName()+"]失败!",t);
			return null;
		}
	}
	
	public void init(){
		if(enableRegister){
			if(methodHeartBeatThread.getState().equals(State.NEW)){
				methodHeartBeatThread.start();
			}
		}
	}
	
	
	private void mergeMethodParam(ServiceUnit serviceUnit){
		if(serviceUnit.getName().equals("StatusService")){
			return ;
		}
		try{
			List<Method> methods = serviceUnit.getMethods();
			for(Method method : methods){
				String signatureJavaClass = method.getSignatureJavaClass();
				if(null != signatureJavaClass && !signatureJavaClass.equals("")){
					Class<?> clazz = Class.forName(signatureJavaClass);
					MethodSignature methodSignature =  (MethodSignature) clazz.newInstance();
					method.getParams().addAll(methodSignature.getMethodParams());
				}
			}
			Session session = sessionFactory.getSession();
			ObjectMapper objectMapper = new ObjectMapper();
			Map<String,Object> data = objectMapper.readValue(objectMapper.writeValueAsString(serviceUnit),Map.class);
			session.cascadeMerge(new ServiceUnitModelDescribe().getName(), data);
		}catch(Throwable t){
			t.printStackTrace();
		}
	}
	
	class RegistryServiceMethodHeartBeatThread implements Runnable{
		@Override
		public void run() {
			while(true){
				try {
					Thread.sleep(methodHeartBeatTime);
					if(registryMethodContext != null && registryMethodContext.values()!=null){
						Iterator<Method> iterator = registryMethodContext.values().iterator();
						while(iterator.hasNext()){
							Method method = iterator.next();
							List<Object> params = new ArrayList<>();
							params.add(serviceId);
							params.add(method.getServiceUnit().getName());
							params.add(method.getName());
							params.add(method.getRegistGroup());
							params.add(host);
							params.add(port);
							Object result = HttpUtil.httpPostServiceUnitRequest(getRegisterAddress()+"/RegisterService/registryServiceMethodHeartBeat", params);
							if(result!=null && result.equals(false)){
								log.error(method.getServiceUnit().getName()+"_"+method.getName()+" heartbeat fail !");
							}else{
								log.info(method.getServiceUnit().getName()+"_"+method.getName()+" heartbeat success !");

							}
							
						}
					}
					
				} catch (InterruptedException e) {
					e.printStackTrace();
					break;
				}catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	public static void setCurrentVar(Object key,Object value){
		if(key != null && value!=null){
			if(varMapContext.get() == null){
				varMapContext.set(new HashMap<Object, Object>());
			}
			varMapContext.get().put(key,value);
		}
		
	}
	
	public static <T> T getCurrentVar(Object key,Class<T> type){
		if(varMapContext.get() != null){
			return varMapContext.get().get(key)==null?null:(T) varMapContext.get().get(key);
		}else{
			return null;
		}
		
	}
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public ApplicationContextBean getApplicationContextBean() {
		return applicationContextBean;
	}

	public void setApplicationContextBean(ApplicationContextBean applicationContextBean) {
		this.applicationContextBean = applicationContextBean;
	}


	public List<MethodInterceptor> getMethodInterceptors() {
		return methodInterceptors;
	}


	public void setMethodInterceptors(List<MethodInterceptor> methodInterceptors) {
		this.methodInterceptors = methodInterceptors;
	}


	public List<LogicInterceptor> getLogicInterceptors() {
		return logicInterceptors;
	}


	public void setLogicInterceptors(List<LogicInterceptor> logicInterceptors) {
		this.logicInterceptors = logicInterceptors;
	}


	public Map<String, MethodHandler> getServiceUnitMethodContext() {
		return serviceUnitMethodContext;
	}

	public Map<String, ServiceUnit> getServiceUnitContext() {
		return serviceUnitContext;
	}

	public boolean isEnableRegister() {
		return enableRegister;
	}

	public void setEnableRegister(boolean enableRegister) {
		this.enableRegister = enableRegister;
	}

	public String getServiceId() {
		return serviceId;
	}

	public void setServiceId(String serviceId) {
		this.serviceId = serviceId;
	}

	public String getRegisterAddress() {
		return registerAddress;
	}

	public void setRegisterAddress(String registerAddress) {
		this.registerAddress = registerAddress;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public Integer getPort() {
		return port;
	}

	public void setPort(Integer port) {
		this.port = port;
	}

	public Long getMethodHeartBeatTime() {
		return methodHeartBeatTime;
	}

	public void setMethodHeartBeatTime(Long methodHeartBeatTime) {
		this.methodHeartBeatTime = methodHeartBeatTime;
	}
	
}
